﻿using System;
using Microsoft.Xna.Framework;

namespace NapazEngine.Framework.Graphics
{
    public class Camera: GameComponent
    {
        private float zoom;
        /// <summary>
        /// The Camera's zoom
        /// </summary>
        public float Zoom
        {
            get
            {
                return zoom;
            }
            set
            {
                zoom = (float)Math.Max(.01, value);
            }
        }

        /// <summary>
        /// The Camera's position
        /// </summary>
        public Vector2 Position { get; set; }

        /// <summary>
        /// The Camera's rotation
        /// </summary>
        public float Rotation { get; set; }

        /// <summary>
        /// The destination the camera should animate to
        /// </summary>
        public Vector2 AnimationDestination { get; private set; }

        /// <summary>
        /// Amount each second that the camera should move
        /// </summary>
        private Vector2 AnimationStepSize { get; set; }

        /// <summary>
        /// The amount of time it should take to animate to that
        /// </summary>
        public float AnimationDuration { get; private set; }

        /// <summary>
        /// Boolean value on whether the camera is animating to a new spot
        /// </summary>
        public bool IsAnimating { get; private set; }

        /// <summary>
        /// Set the window size for the camera
        /// </summary>
        public Vector2 WindowSize { get; set; }

        public Camera(Game game) : base(game)
        {
            IsAnimating = false;
            Position = Vector2.Zero;
            Rotation = 0;
            Zoom = 1;
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            if (IsAnimating)
            {
                Vector2 movementThisUpdate = AnimationStepSize * (float)(gameTime.ElapsedGameTime.TotalMilliseconds);
                if(movementThisUpdate.Length() < (AnimationDestination - Position).Length())
                {
                    Position += movementThisUpdate;
                }
                else
                {
                    Position = AnimationDestination;
                    IsAnimating = false;
                }    
            }
        }

        /// <summary>
        /// Gets the transformation produced from the current camera settings
        /// </summary>
        /// <param name="windowWidth">Width of the game window</param>
        /// <param name="windowHeight">Height of the game window</param>
        /// <returns></returns>
        public Matrix GetTransformation(float windowWidth, float windowHeight)
        {
            Matrix transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)) *                 //performs the position translation
                               Matrix.CreateRotationZ(Rotation) *                                                   //performs the rotation
                               Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *                                     //scales for zoom
                               Matrix.CreateTranslation(new Vector3(windowWidth * 0.5f, windowHeight * 0.5f, 0));   //centers on the screen
            return transform;
        }

        /// <summary>
        /// Gets the transformation produced from the current camera settings
        /// </summary>
        public Matrix Transformation
        {
            get
            {
                Matrix transform = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)) *                 //performs the position translation
                               Matrix.CreateRotationZ(Rotation) *                                                   //performs the rotation
                               Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)) *                                     //scales for zoom
                               Matrix.CreateTranslation(new Vector3(WindowSize.X * 0.5f, WindowSize.Y * 0.5f, 0));   //centers on the screen
                return transform;
            }
        }


        /// <summary>
        /// Force the Camera to animate towards a position over a set time span
        /// </summary>
        /// <param name="destination">Destination to animate to</param>
        /// <param name="duration">timespan (in Milliseconds) of the animation</param>
        public void ApplyAnimation(Vector2 destination, float duration)
        {
            AnimationDestination = destination;
            AnimationDuration = duration;
            IsAnimating = true;

            Vector2 directionToMove = AnimationDestination - Position;
            AnimationStepSize = (directionToMove / AnimationDuration);
        }

        public void Move(Vector2 vectorToMove)
        {
            IsAnimating = false;
            Position += vectorToMove;
        }
    }
}
